using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;

namespace AreaBuilder {
    internal class ImportArea {
        private readonly Regex AreaParams =
            new Regex(
                @".*?(?<file>\w+\.are)~.*?\0?(?<title>.*?)~.*?(?<minLevel>\-?\d+)\s+(?<maxLevel>\-?\d+)\s+(?<author>.*?)~.*?(?<minVnum>\d+)\s+(?<maxVnum>\d+)\s+0",
                RegexOptions.Compiled);

        /// <summary>
        /// Regular expressions used for parsing the huge .are files.
        /// Compiled for speed. Yeah, more memory, but it's faster. Sue me.
        /// </summary>
        private readonly Regex AREASection = new Regex(@"#AREA\s*(?<areaSection>.*? \d+ 0\0\0)", RegexOptions.Compiled);

        private readonly Regex HelpParams =
            new Regex(@"(?<minLevel>\d+)\s+(?<keywords>[^~]+)~\s*\0+\s*(?<description>[^~]+)~", RegexOptions.Compiled);

        private readonly Regex HELPSection = new Regex(@"#HELPS\s*(?<helpSection>.*?0 \$~)", RegexOptions.Compiled);

        private readonly Regex MobFlag =
            new Regex(
                @"F\s+(?<flag>act(ion)?|aff(ect)?|off(ensive)?|imm(unity)?|res(istance)?|vul(nerability)?|for(m)?|par(ts)?)\s(?<value>\w+)\s*\0+\s*",
                RegexOptions.Compiled);

        /////////////////////////

        private readonly Regex MobInfo =
            new Regex(
                @"#?(?<vnum>\d+)\s*\0*(?<keywords>.+)~\s*\0*(?<sname>.+)~\s*\0*(?<sdesc>.+)~\s*\0*(?<ldesc>.+)~\s*\0*(?<race>.+)~\s*\0*(?<act>[A-Za-z0]+)\s+(?<affect>[A-Za-z0]+)\s+(?<affect2>[A-Za-z0]+)\s+(?<alignment>\-?\d+)\s+(?<group>\d+)\s*\0*(?<level>\d+)\s+(?<class>\d{1,2})\s+(?<castLvl>\d)\s+(?<hitDice>.+)\s+(?<manaDice>.+)\s+(?<damBonus>\-?\d+)\s+(?<damDice>.+)\s+(?<damType>[^\0]+)\s*\0*(?<pierce>\-?\d+)\s+(?<bash>\-?\d+)\s+(?<slash>\-?\d+)\s+(?<magical>\-?\d+)\s*\0*(?<offensive>[A-Za-z0]+)\s+(?<immune>[A-Za-z0]+)\s+(?<resists>[A-Za-z0]+)\s+(?<vulnerabilities>[A-Za-z0]+)\s*\0*(?<start>\w+)\s+(?<default>\w+)\s+(?<sex>\w+)\s+(?<treasure>\d+)\s*\0*(?:(?<form>\w+)\s+(?<part>\w+)\s+(?<size>\d{1,2})\s+(?<material>[^\0]+))?(?:\s*\0+\s*C\s+(?<clan>[0-7]))?\s*\0*",
                RegexOptions.Compiled);

        private readonly Regex MobParams = new Regex(@"#(?<mobInfo>\d+.*?)(?=#)", RegexOptions.Compiled);

        private readonly Regex MobProgs = new Regex(@"M\s+(?<trigger>[a-z]+)\s+(?<prog>\d+)\s+(?<argument>[^~]+)~",
                                                    RegexOptions.Compiled);

        private readonly Regex MOBPROGSection = new Regex(@"#MOBPROGS\s*(?<progSection>.*?#0)", RegexOptions.Compiled);
        private readonly Regex MOBSection = new Regex(@"#MOBILES\s*(?<mobSection>.*?#0)", RegexOptions.Compiled);

        private readonly Regex ObjAffects =
            new Regex(@"(?<! )\bA\s*\0+(?<affectType>\d+)\s+(?<affectValue>\-?\d+)\s*\0+", RegexOptions.Compiled);

        private readonly Regex OBJECTSection = new Regex(@"#OBJECTS\s*(?<objSection>.*?#0)", RegexOptions.Compiled);

        private readonly Regex ObjExtra = new Regex(
            @"\0+E\s*\0+\s*(?<extraKeyword>[^~]+)~\s*\0+\s*(?<extraDesc>[^~]+)~", RegexOptions.Compiled);

        private readonly Regex ObjFlags =
            new Regex(@"\0*F\s*\0+\s*(?<type>[A|V|I|R])\s+(?<location>\d+)\s+(?<modifier>\d+)\s+(?<bits>\w+)\0*",
                      RegexOptions.Compiled);

        private readonly Regex ObjInfo =
            new Regex(
                @"#?(?<vnum>\d+)\s*\0*(?<keywords>[^\0~]+)~\s*\0*(?<sname>[^~]+)~\s*\0*(?<desc>[^~]+)~\s*\0*(?<material>[^~]+)~\s*\0*(?<type>\w+)\s+(?<extraFlags>\w+)\s+(?<wearFlags>\w+)\s*\0*(?<V0>([^' ]+|'(?:[^']*')))\s+(?<V1>([^' ]+|'(?:[^']*')))\s+(?<V2>([^' ]+|'(?:[^']*')))\s+(?<V3>([^' ]+|'(?:[^']*')))\s+(?<V4>([^'\0 ]+|'(?:[^'\0]*')))\s*(?<V5>([^'\0 ]*|'(?:[^'\0]*')))\s*\0*(?<level>\d+)\s+(?<minSize>\-?\d+)\s+(?<maxSize>\-?\d+)\s+(?<weight>\-?\d+)\s+(?<cost>\d+)\s+[A-Z]\s*\0*(?:(?<trapLevel>\d+)\s+(?<trapTrigger>\w+)\s+(?<trapDamage>\-?\d+)\s+(?<trapCharges>\d+))?",
                RegexOptions.Compiled);

        private readonly Regex ObjParams = new Regex(@"#(?<objInfo>\d+.+?)(?=#)", RegexOptions.Compiled);

        private readonly Regex ObjTriggers =
            new Regex(
                @"S\s*\0+(?<triggerType>\w+)\s+(?<triggerChance>\d+)\s+-?(?<triggerCount>\d+)\s+(?<triggerSpell>[^~\0]+)~\0*",
                RegexOptions.Compiled);

        private readonly Regex ProgInfo = new Regex(@"#(?<vnum>\d+)\s*\0*(?<program>[^~]+)~", RegexOptions.Compiled);

        /////////////////////////
        private readonly Regex ResetParams =
            new Regex(
                @"(?<type>[M|O|P|G|E|D|R|S])\s+(?<slot>\d)\s+(?<vnum>\d+)\s+(?<limit>\-?\d+)(\s+(?<roomvnum>\-?\d+)\s+(?<roomlimit>\-?\d+)|\s+(?<roomvnum>\-?\d*)|\s*)\s*[^\0]*\0",
                RegexOptions.Compiled);

        private readonly Regex RESETSection = new Regex(@"#RESETS\s*(?<resetSection>.*?\0S\0)", RegexOptions.Compiled);

        /////////////////////////

        private readonly Regex RoomExit =
            new Regex(
                @"D(?<exitDirection>[0-5])\s*\0+\s*(?<exitDesc>[^~]*)~\s*\0+\s*(?<doorKeyphrase>[^\0~]*)~\s*\0+\s*(?<doorState>\-?\d+)\s+(?<keyVnum>\-?\d+)\s+(?<linkVnum>\-?\d+)\s*\0*",
                RegexOptions.Compiled);

        private readonly Regex RoomExtra = new Regex(@"E\s*\0+\s*(?<extraKeyword>[^~]+)~\s*\0+\s*(?<extraDesc>[^~]+)~",
                                                     RegexOptions.Compiled);

        private readonly Regex RoomHP = new Regex(@"M\s+(?<manaValue>\d+)\s+H\s+(?<hpValue>\d+)\s*\0+",
                                                  RegexOptions.Compiled);

        private readonly Regex RoomParams =
            new Regex(
                @"#(?<vnum>\d+)\s*\0+(?<title>[^~]+)~\s*\0*(?<description>[^~]+)\s*\0*~\s*\0*(?<xxvalue>\d+)\s+(?<roomFlags>\w+)\s+(?<sectorFlags>\w+)\s*\0+(?<everythingElse>[^#]+)",
                RegexOptions.Compiled);

        private readonly Regex ROOMSection = new Regex(@"#ROOMS\s*(?<roomSection>.*?)#0", RegexOptions.Compiled);

        private readonly Regex ShopParams =
            new Regex(
                @"(?<vnum>\d+)\s+(?<item0>\d+)\s+(?<item1>\d+)\s+(?<item2>\d+)\s+(?<item3>\d+)\s+(?<item4>\d+)\s+(?<pBuy>\d+)\s+(?<pSell>\d+)\s+(?<startHour>\d+)\s+(?<endHour>\d+).*?\0",
                RegexOptions.Compiled);

        private readonly Regex SHOPSection = new Regex(@"#SHOPS\s*(?<shopSection>.*?)0\0\0", RegexOptions.Compiled);

        private readonly Regex SPECIALSection = new Regex(@"#SPECIALS\s*(?<specialSection>.*?\0)S\0",
                                                          RegexOptions.Compiled);

        private readonly Regex SpecParams = new Regex(@"M\s+(?<vnum>\d+)\s+(?<spec>[A-Za-z_]+).*?\0",
                                                      RegexOptions.Compiled);

        public bool Success;

        /////////////////////////

        /// <summary>
        /// Imports a ROM2b4 .are file and exports it to XML format.
        /// This area builder uses XML format for... well, everything.
        /// </summary>
        /// <param name="fileRead">.ARE file to read from.</param>
        /// <param name="area">Area object to modify.</param>
        public ImportArea(String fileRead, Area area) {
            var fileIn = new StringBuilder();
            StreamReader re = File.OpenText(fileRead);
            String input;

            while ((input = re.ReadLine()) != null) {
                fileIn.Append(input.Trim());
                fileIn.Append('\0');
            }

            re.Close();

            String file = fileIn.ToString();

            area.File = Path.GetFileName(fileRead);

            Match areaSection = AREASection.Match(file);
            Match mobSection = MOBSection.Match(file);
            Match progSection = MOBPROGSection.Match(file);
            Match objSection = OBJECTSection.Match(file);
            Match resetSection = RESETSection.Match(file);
            Match specialSection = SPECIALSection.Match(file);
            Match shopSection = SHOPSection.Match(file);
            Match roomSection = ROOMSection.Match(file);
            Match helpSectionc = HELPSection.Match(file);

            doArea(ref areaSection, area);
            doHelp(ref helpSectionc, area);
            doMobs(ref mobSection, area);
            doMobprogs(ref progSection, area);
            doObjects(ref objSection, area);
            doRooms(ref roomSection, area);
            doResets(ref resetSection, area);
            doShops(ref shopSection, area);
            doSpecial(ref specialSection, area);

            Console.ReadLine();
            Success = true;
        }

        private void doArea(ref Match m, Area area) {
            if (m.Success) {
                m = AreaParams.Match(m.Groups["areaSection"].Value);

                if (m.Success) {
                    area.Title = m.Groups["title"].Value;
                    area.Author = m.Groups["author"].Value;
                    area.MinLevel = Int32.Parse(m.Groups["minLevel"].Value);
                    area.MaxLevel = Int32.Parse(m.Groups["maxLevel"].Value);
                    area.MinVnum = Int32.Parse(m.Groups["minVnum"].Value);
                    area.MaxVnum = Int32.Parse(m.Groups["maxVnum"].Value);
                }
            }
        }

        private void doMobs(ref Match m, Area area) {
            if (m.Success) {
                m = MobParams.Match(m.Groups["mobSection"].Value);

                while (m.Success) {
                    Match k = MobInfo.Match(m.Groups["mobInfo"].Value);

                    var mob = new Mobile();

                    if (k.Success) {
                        mob.VNum = Int32.Parse(k.Groups["vnum"].Value);
                        mob.Keywords = k.Groups["keywords"].Value;
                        mob.Long = Regex.Replace(k.Groups["ldesc"].Value, "\0$", "").Replace("\0", "\n");
                        mob.Short = k.Groups["sdesc"].Value.Replace("\0", String.Empty);
                        mob.Name = k.Groups["sname"].Value;
                        mob.Race = k.Groups["race"].Value;
                        mob.Action = k.Groups["act"].Value;
                        mob.Affects = k.Groups["affect"].Value;
                        mob.MoreAffects = k.Groups["affect2"].Value;
                        mob.Align = Int32.Parse(k.Groups["alignment"].Value);
                        mob.MobGroup = Int32.Parse(k.Groups["group"].Value);
                        mob.Level = Int32.Parse(k.Groups["level"].Value);
                        mob.Class = Int32.Parse(k.Groups["class"].Value);
                        mob.CastLevel = Int32.Parse(k.Groups["castLvl"].Value);
                        mob.HitDice = k.Groups["hitDice"].Value;
                        mob.ManaDice = k.Groups["manaDice"].Value;
                        mob.DamageBonus = Int32.Parse(k.Groups["damBonus"].Value);
                        mob.DamageDice = k.Groups["damDice"].Value;
                        mob.DamageType = k.Groups["damType"].Value;
                        mob.Pierce = Int32.Parse(k.Groups["pierce"].Value);
                        mob.Bash = Int32.Parse(k.Groups["bash"].Value);
                        mob.Slash = Int32.Parse(k.Groups["slash"].Value);
                        mob.Magical = Int32.Parse(k.Groups["magical"].Value);
                        mob.Offensive = k.Groups["offensive"].Value;
                        mob.Immune = k.Groups["immune"].Value;
                        mob.Resist = k.Groups["resists"].Value;
                        mob.Vulnerable = k.Groups["vulnerabilities"].Value;
                        mob.StartPos = k.Groups["start"].Value;
                        mob.DefaultPos = k.Groups["default"].Value;
                        mob.Sex = k.Groups["sex"].Value;
                        mob.Treasure = Int32.Parse(k.Groups["treasure"].Value);

                        if (!String.IsNullOrEmpty(k.Groups["form"].Value)) {
                            mob.Form = k.Groups["form"].Value;
                            mob.Part = k.Groups["part"].Value;
                            mob.Size = Int32.Parse(k.Groups["size"].Value);
                            mob.Material = k.Groups["material"].Value;
                        }

                        if (!String.IsNullOrEmpty(k.Groups["clan"].Value))
                            mob.Clan = k.Groups["clan"].Value;

                        area.AddMob(mob);
                    }

                    k = MobProgs.Match(m.Groups["mobInfo"].Value);

                    while (k.Success) {
                        var mp = new MobProgLink {
                            Trigger = k.Groups["trigger"].Value,
                            Program = Int32.Parse(k.Groups["prog"].Value),
                            Argument = k.Groups["argument"].Value
                        };
                        k = k.NextMatch();

                        mob.AddProg(mp);
                    }

                    k = MobFlag.Match(m.Groups["mobInfo"].Value);

                    while (k.Success) {
                        var mf = new MobFlag { FlagType = k.Groups["flag"].Value, FlagValue = k.Groups["value"].Value };

                        k = k.NextMatch();

                        mob.AddFlag(mf);
                    }

                    m = m.NextMatch();
                }
            }
        }

        private void doMobprogs(ref Match m, Area area) {
            if (m.Success) {
                m = ProgInfo.Match(m.Groups["progSection"].Value);

                while (m.Success) {
                    var mp = new MobProg {
                        VNum = Int32.Parse(m.Groups["vnum"].Value),
                        Program = Regex.Replace(m.Groups["program"].Value, "\0$", "").Replace("\0", "\n")
                    };
                    m = m.NextMatch();

                    area.AddProg(mp);
                }
            }
        }

        private void doObjects(ref Match m, Area area) {
            if (m.Success) {
                m = ObjParams.Match(m.Groups["objSection"].Value);

                while (m.Success) {
                    Match k = ObjInfo.Match(m.Groups["objInfo"].Value);

                    if (k.Success) {
                        var i = new Item {
                            VNum = Int32.Parse(k.Groups["vnum"].Value),
                            Keywords = k.Groups["keywords"].Value,
                            Name = k.Groups["sname"].Value,
                            Description = Regex.Replace(k.Groups["desc"].Value, "\0$", "").Replace("\0", "\n"),
                            Material = k.Groups["material"].Value.Replace("\0", ""),
                            ItemType = k.Groups["type"].Value,
                            ExtraFlags = k.Groups["extraFlags"].Value,
                            WearFlags = k.Groups["wearFlags"].Value,
                            V0 = k.Groups["V0"].Value,
                            V1 = k.Groups["V1"].Value,
                            V2 = k.Groups["V2"].Value,
                            V3 = k.Groups["V3"].Value,
                            V4 = k.Groups["V4"].Value,
                            Level = Int32.Parse(k.Groups["level"].Value),
                            MinSize = Int32.Parse(k.Groups["minSize"].Value),
                            MaxSize = Int32.Parse(k.Groups["maxSize"].Value),
                            Weight = Int32.Parse(k.Groups["weight"].Value),
                            Cost = Int32.Parse(k.Groups["cost"].Value)
                        };

                        if (!String.IsNullOrEmpty(k.Groups["V5"].Value))
                            i.V5 = k.Groups["V5"].Value;

                        if (!String.IsNullOrEmpty(k.Groups["trapLevel"].Value)) {
                            i.TrapLevel = k.Groups["trapLevel"].Value;
                            i.TrapTrigger = k.Groups["trapTrigger"].Value;
                            i.TrapDamage = k.Groups["trapDamage"].Value;
                            i.TrapCharges = k.Groups["trapCharges"].Value;
                        }

                        Match l = ObjAffects.Match(m.Groups["objInfo"].Value);
                        while (l.Success) {
                            var ia = new ItemAffect {
                                AffectType = Int32.Parse(l.Groups["affectType"].Value),
                                AffectValue = Int32.Parse(l.Groups["affectValue"].Value)
                            };
                            l = l.NextMatch();

                            i.addAffect(ia);
                        }

                        l = ObjExtra.Match(m.Groups["objInfo"].Value);
                        if (l.Success) {
                            i.ExtraKeys = l.Groups["extraKeyword"].Value;
                            i.ExtraDesc = Regex.Replace(l.Groups["extraDesc"].Value, "\0$", "").Replace("\0", "\n");
                        }

                        l = ObjFlags.Match(m.Groups["objInfo"].Value);
                        while (l.Success) {
                            var iflag = new ItemFlag {
                                FlagType = l.Groups["type"].Value,
                                FlagLocation = Int32.Parse(l.Groups["location"].Value),
                                FlagValue = Int32.Parse(l.Groups["modifier"].Value),
                                FlagBits = l.Groups["bits"].Value
                            };
                            l = l.NextMatch();

                            i.addFlag(iflag);
                        }

                        l = ObjTriggers.Match(m.Groups["objInfo"].Value);
                        while (l.Success) {
                            var it = new ItemTrigger {
                                TriggerType = l.Groups["triggerType"].Value,
                                TriggerSpell = l.Groups["triggerSpell"].Value,
                                TriggerCount = Int32.Parse(l.Groups["triggerCount"].Value),
                                TriggerChance = Int32.Parse(l.Groups["triggerChance"].Value)
                            };
                            l = l.NextMatch();

                            i.addTrigger(it);
                        }

                        area.AddItem(i);
                    }

                    m = m.NextMatch();
                }
            }
        }

        private void doResets(ref Match m, Area area) {
            if (m.Success) {
                m = ResetParams.Match(m.Groups["resetSection"].Value);

                while (m.Success) {
                    var r = new Reset {
                        Type = m.Groups["type"].Value,
                        Slot = Int32.Parse(m.Groups["slot"].Value),
                        VNum = Int32.Parse(m.Groups["vnum"].Value),
                        Limit = Int32.Parse(m.Groups["limit"].Value),
                        VNumWear = m.Groups["roomvnum"].Value,
                        ExtraParam = m.Groups["roomlimit"].Value
                    };
                    m = m.NextMatch();

                    area.AddReset(r);
                }
            }
        }

        private void doSpecial(ref Match m, Area area) {
            if (m.Success) {
                m = SpecParams.Match(m.Groups["specialSection"].Value);

                while (m.Success) {
                    var s = new Special { Type = m.Groups["spec"].Value, VNum = Int32.Parse(m.Groups["vnum"].Value) };
                    m = m.NextMatch();

                    area.AddSpecial(s);
                }
            }
        }

        private void doShops(ref Match m, Area area) {
            if (m.Success) {
                m = ShopParams.Match(m.Groups["shopSection"].Value);

                while (m.Success) {
                    var s = new Shop {
                        VNum = Int32.Parse(m.Groups["vnum"].Value),
                        First = Int32.Parse(m.Groups["item0"].Value),
                        Second = Int32.Parse(m.Groups["item1"].Value),
                        Third = Int32.Parse(m.Groups["item2"].Value),
                        Fourth = Int32.Parse(m.Groups["item3"].Value),
                        Fifth = Int32.Parse(m.Groups["item4"].Value),
                        Buy = Int32.Parse(m.Groups["pBuy"].Value),
                        Sell = Int32.Parse(m.Groups["pSell"].Value),
                        StartHour = Int32.Parse(m.Groups["startHour"].Value),
                        EndHour = Int32.Parse(m.Groups["endHour"].Value)
                    };
                    m = m.NextMatch();

                    area.AddShop(s);
                }
            }
        }

        private void doRooms(ref Match m, Area area) {
            if (m.Success) {
                m = RoomParams.Match(m.Groups["roomSection"].Value);

                while (m.Success) {
                    var r = new Room();
                    Match l = RoomHP.Match(m.Groups["everythingElse"].Value);
                    Match k = RoomExit.Match(m.Groups["everythingElse"].Value);
                    Match i = RoomExtra.Match(m.Groups["everythingElse"].Value);

                    r.VNum = Int32.Parse(m.Groups["vnum"].Value);
                    r.Title = m.Groups["title"].Value;
                    r.Description = Regex.Replace(m.Groups["description"].Value, "\0$", "").Replace("\0", "\n");
                    r.XXValue = Int32.Parse(m.Groups["xxvalue"].Value);
                    r.RoomFlags = m.Groups["roomFlags"].Value;
                    r.SectorFlags = m.Groups["sectorFlags"].Value;
                    m = m.NextMatch();

                    if (l.Success) {
                        r.ManaRegen = Int32.Parse(l.Groups["manaValue"].Value);
                        r.HPRegen = Int32.Parse(l.Groups["hpValue"].Value);
                    }

                    while (k.Success) {
                        var rex = new RoomExit {
                            Direction = Int32.Parse(k.Groups["exitDirection"].Value),
                            Description = Regex.Replace(k.Groups["exitDesc"].Value, "\0$", "").Replace("\0", "\n"),
                            Keyphrase = k.Groups["doorKeyphrase"].Value,
                            DoorState = Int32.Parse(k.Groups["doorState"].Value),
                            KeyVNum = Int32.Parse(k.Groups["keyVnum"].Value),
                            LinkVNum = Int32.Parse(k.Groups["linkVnum"].Value)
                        };
                        k = k.NextMatch();

                        r.addExit(rex);
                    }

                    while (i.Success) {
                        var rex = new RoomExtra {
                            Keyword = i.Groups["extraKeyword"].Value,
                            Description = Regex.Replace(i.Groups["extraDesc"].Value, "\0$", "").Replace("\0", "\n")
                        };
                        i = i.NextMatch();

                        r.addExtra(rex);
                    }

                    area.AddRoom(r);
                }
            }
        }

        private void doHelp(ref Match m, Area area) {
            if (m.Success) {
                m = HelpParams.Match(m.Groups["helpSection"].Value);

                while (m.Success) {
                    var h = new Help {
                        MinLevel = Int32.Parse(m.Groups["minLevel"].Value),
                        Keywords = m.Groups["keywords"].Value,
                        Description = Regex.Replace(m.Groups["description"].Value, "\0$", "").Replace("\0", "\n")
                    };
                    m = m.NextMatch();

                    area.AddHelp(h);
                }
            }
        }
    }
}